#pragma once

#include <common.h>
#include <riscv/comp_unit.h>
#include <riscv/func.h>
#include <riscv/block.h>
#include <riscv/inst.h>
#include <riscv/regs.h>
#include <riscv/float_number.h>
#include <type.h>

namespace riscv {
  class Genarator;

  class Genarator {
  public:
    Genarator(CompUnit * comp_unit);

    Reg alloc_temp_reg();
    FReg alloc_temp_freg();
    std::set<Reg> get_alloced_reg();
    void free_temp_reg(Reg temp_reg);
    void free_temp_reg(FReg temp_reg);

    Global * create_global(std::string name, int size, int init_number);
    Func * create_func(const std::string & name);
    FloatNumber * create_float_number(float number);

    Block * create_block(Func & func);
    Block * create_block_begin(Func & func);
    Block * create_block_end(Func & func);

    void gen_inst_addi(std::vector<Inst*> & inst_list, Reg rd, Reg src, int imm);

    void gen_inst_add(std::vector<Inst*> & inst_list, Reg rd, Reg src1, Reg src2);
    void gen_inst_sub(std::vector<Inst*> & inst_list, Reg rd, Reg src1, Reg src2);
    void gen_inst_mul(std::vector<Inst*> & inst_list, Reg rd, Reg src1, Reg src2);
    void gen_inst_div(std::vector<Inst*> & inst_list, Reg rd, Reg src1, Reg src2);

    void gen_inst_sd(std::vector<Inst*> & inst_list, Reg rd, int off, Reg src);
    void gen_inst_sw(std::vector<Inst*> & inst_list, Reg rd, int off, Reg src);
    void gen_inst_ld(std::vector<Inst*> & inst_list, Reg rd, int off, Reg src);
    void gen_inst_lw(std::vector<Inst*> & inst_list, Reg rd, int off, Reg src);

    void gen_inst_j(std::vector<Inst*> & inst_list, Block * block);
    void gen_inst_bne(std::vector<Inst*> & inst_list, Reg src1, Reg src2, Block * block);
    void gen_inst_beq(std::vector<Inst*> & inst_list, Reg src1, Reg src2, Block * block);

    void gen_inst_li(std::vector<Inst*> & inst_list, Reg reg, int imm);
    void gen_inst_mv(std::vector<Inst*> & inst_list, Reg rd, Reg src);
    void gen_inst_call(std::vector<Inst*> & inst_list, Func * func);
    void gen_inst_ret(std::vector<Inst*> & inst_list);
    void gen_inst_lla(std::vector<Inst*> & inst_list, Reg rd, Global * global_src);
    void gen_inst_lla(std::vector<Inst*> & inst_list, Reg rd, FloatNumber * float_number);

    void gen_inst_sext_w(std::vector<Inst*> & inst_list, Reg rd, Reg src);

    void gen_inst_fadd_d(std::vector<Inst*> & inst_list, FReg rd, FReg src1, FReg src2);
    void gen_inst_fsub_d(std::vector<Inst*> & inst_list, FReg rd, FReg src1, FReg src2);
    void gen_inst_fmul_d(std::vector<Inst*> & inst_list, FReg rd, FReg src1, FReg src2);
    void gen_inst_fdiv_d(std::vector<Inst*> & inst_list, FReg rd, FReg src1, FReg src2);

    void gen_inst_fsd(std::vector<Inst*> & inst_list, FReg rd, int off, Reg src);
    void gen_inst_fsw(std::vector<Inst*> & inst_list, FReg rd, int off, Reg src);
    void gen_inst_fld(std::vector<Inst*> & inst_list, FReg rd, int off, Reg src);
    void gen_inst_flw(std::vector<Inst*> & inst_list, FReg rd, int off, Reg src);

    void gen_inst_fmv_s(std::vector<Inst*> & inst_list, FReg rd, FReg src);
    void gen_inst_fmv_d(std::vector<Inst*> & inst_list, FReg rd, FReg src);
    void gen_inst_fmv_d_x(std::vector<Inst*> & inst_list, FReg rd, Reg src);
    void gen_inst_fmv_x_d(std::vector<Inst*> & inst_list, Reg rd, FReg src);

    void gen_inst_fcvt_d_s(std::vector<Inst*> & inst_list, FReg rd, FReg src);
    void gen_inst_fcvt_s_d(std::vector<Inst*> & inst_list, FReg rd, FReg src);
    void gen_inst_fcvt_l_d(std::vector<Inst*> & inst_list, Reg rd, FReg src, FcvtRm rm);
    void gen_inst_fcvt_d_l(std::vector<Inst*> & inst_list, FReg rd, Reg src, FcvtRm rm);

  private:
    CompUnit * comp_unit;
    std::map<Reg, bool> temp_reg_map;
    std::map<FReg, bool> temp_freg_map;

    int float_number_i;

    int block_i;
    int block_begin_i;
    int block_end_i;

  };
}
